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  }