github.com/opentofu/opentofu@v1.7.1/internal/getproviders/didyoumean_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package getproviders 7 8 import ( 9 "context" 10 "testing" 11 12 svchost "github.com/hashicorp/terraform-svchost" 13 "github.com/opentofu/opentofu/internal/addrs" 14 ) 15 16 func TestMissingProviderSuggestion(t *testing.T) { 17 // Most of these test cases rely on specific "magic" provider addresses 18 // that are implemented by the fake registry source returned by 19 // testRegistrySource. Refer to that function for more details on how 20 // they work. 21 22 t.Run("happy path", func(t *testing.T) { 23 ctx := context.Background() 24 source, _, close := testRegistrySource(t) 25 defer close() 26 27 // testRegistrySource handles -/legacy as a valid legacy provider 28 // lookup mapping to legacycorp/legacy. 29 legacyAddr := addrs.NewDefaultProvider("legacy") 30 got := MissingProviderSuggestion( 31 ctx, 32 addrs.NewDefaultProvider("legacy"), 33 source, 34 Requirements{ 35 legacyAddr: MustParseVersionConstraints(">= 1.0.0"), 36 }, 37 ) 38 39 want := addrs.Provider{ 40 Hostname: defaultRegistryHost, 41 Namespace: "legacycorp", 42 Type: "legacy", 43 } 44 if got != want { 45 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 46 } 47 }) 48 t.Run("provider moved", func(t *testing.T) { 49 ctx := context.Background() 50 source, _, close := testRegistrySource(t) 51 defer close() 52 53 // testRegistrySource handles -/moved as a valid legacy provider 54 // lookup mapping to hashicorp/moved but with an additional "redirect" 55 // to acme/moved. This mimics how for some providers there is both 56 // a copy under terraform-providers for v0.12 compatibility _and_ a 57 // copy in some other namespace for v0.13 or later to use. Our naming 58 // suggestions ignore the v0.12-compatible one and suggest the 59 // other one. 60 moved := addrs.NewDefaultProvider("moved") 61 want := addrs.Provider{ 62 Hostname: defaultRegistryHost, 63 Namespace: "acme", 64 Type: "moved", 65 } 66 67 got := MissingProviderSuggestion( 68 ctx, 69 moved, 70 source, 71 Requirements{ 72 moved: MustParseVersionConstraints(">= 1.0.0"), 73 }, 74 ) 75 76 if got != want { 77 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 78 } 79 80 // If a provider has moved, but there's provider requirements 81 // for something of the same type, we'll return that one 82 // and skip the legacy lookup process. In practice, 83 // hopefully this is also "acme" but it's "zcme" here to 84 // exercise the codepath 85 want2 := addrs.Provider{ 86 Hostname: defaultRegistryHost, 87 Namespace: "zcme", 88 Type: "moved", 89 } 90 got2 := MissingProviderSuggestion( 91 ctx, 92 moved, 93 source, 94 Requirements{ 95 moved: MustParseVersionConstraints(">= 1.0.0"), 96 want2: MustParseVersionConstraints(">= 1.0.0"), 97 }, 98 ) 99 100 if got2 != want2 { 101 t.Errorf("wrong result\ngot: %s\nwant: %s", got2, want2) 102 } 103 }) 104 t.Run("invalid response", func(t *testing.T) { 105 ctx := context.Background() 106 source, _, close := testRegistrySource(t) 107 defer close() 108 109 // testRegistrySource handles -/invalid by returning an invalid 110 // provider address, which MissingProviderSuggestion should reject 111 // and behave as if there was no suggestion available. 112 want := addrs.NewDefaultProvider("invalid") 113 got := MissingProviderSuggestion( 114 ctx, 115 want, 116 source, 117 Requirements{ 118 want: MustParseVersionConstraints(">= 1.0.0"), 119 }, 120 ) 121 if got != want { 122 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 123 } 124 }) 125 t.Run("another registry", func(t *testing.T) { 126 ctx := context.Background() 127 source, _, close := testRegistrySource(t) 128 defer close() 129 130 // Because this provider address isn't on registry.opentofu.org, 131 // MissingProviderSuggestion won't even attempt to make a suggestion 132 // for it. 133 want := addrs.Provider{ 134 Hostname: svchost.Hostname("example.com"), 135 Namespace: "whatever", 136 Type: "foo", 137 } 138 got := MissingProviderSuggestion( 139 ctx, 140 want, 141 source, 142 Requirements{ 143 want: MustParseVersionConstraints(">= 1.0.0"), 144 }, 145 ) 146 if got != want { 147 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 148 } 149 }) 150 t.Run("another namespace", func(t *testing.T) { 151 ctx := context.Background() 152 source, _, close := testRegistrySource(t) 153 defer close() 154 155 // Because this provider address isn't in 156 // registry.opentofu.org/hashicorp/..., MissingProviderSuggestion 157 // will provide the same addr since there's no alternative in Requirements 158 want := addrs.Provider{ 159 Hostname: defaultRegistryHost, 160 Namespace: "whatever", 161 Type: "foo", 162 } 163 got := MissingProviderSuggestion( 164 ctx, 165 want, 166 source, 167 Requirements{ 168 want: MustParseVersionConstraints(">= 1.0.0"), 169 }, 170 ) 171 if got != want { 172 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 173 } 174 175 // If there is a provider required that has the same type, 176 // but different namespace, we can suggest that 177 foo := addrs.Provider{ 178 Hostname: defaultRegistryHost, 179 Namespace: "hashicorp", 180 Type: "foo", 181 } 182 realFoo := addrs.Provider{ 183 Hostname: defaultRegistryHost, 184 Namespace: "acme", 185 Type: "foo", 186 } 187 got2 := MissingProviderSuggestion( 188 ctx, 189 foo, 190 source, 191 Requirements{ 192 foo: MustParseVersionConstraints(">= 1.0.0"), 193 realFoo: MustParseVersionConstraints(">= 1.0.0"), 194 }, 195 ) 196 if got2 != realFoo { 197 t.Errorf("wrong result\ngot: %s\nwant: %s", got2, realFoo) 198 } 199 }) 200 }