move providers lock command to new arguments / views architecture

This commit is contained in:
Daniel Schmidt 2026-02-10 17:19:52 +01:00
parent 27770ee805
commit c7f53388bf
No known key found for this signature in database
GPG key ID: 377C3A4D62FBBBE2
6 changed files with 511 additions and 79 deletions

View file

@ -0,0 +1,70 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package arguments
import (
"github.com/hashicorp/terraform/internal/tfdiags"
)
// ProvidersLock represents the command-line arguments for the providers lock command.
type ProvidersLock struct {
// Platforms is the list of target platforms to request package checksums for.
Platforms FlagStringSlice
// FSMirrorDir is the filesystem mirror directory to consult instead of the
// origin registry.
FSMirrorDir string
// NetMirrorURL is the network mirror base URL to consult instead of the
// origin registry.
NetMirrorURL string
// TestDirectory is the directory containing test files, defaults to "tests".
TestDirectory string
// EnablePluginCache enables the usage of the globally configured plugin cache.
EnablePluginCache bool
// Providers is the list of provider source addresses given as positional arguments.
Providers []string
}
// ParseProvidersLock processes CLI arguments, returning a ProvidersLock value and error
// diagnostics. If there are any diagnostics present, a ProvidersLock value is still
// returned representing the best effort interpretation of the arguments.
func ParseProvidersLock(args []string) (*ProvidersLock, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
result := &ProvidersLock{
TestDirectory: "tests",
}
cmdFlags := defaultFlagSet("providers lock")
cmdFlags.Var(&result.Platforms, "platform", "target platform")
cmdFlags.StringVar(&result.FSMirrorDir, "fs-mirror", "", "filesystem mirror directory")
cmdFlags.StringVar(&result.NetMirrorURL, "net-mirror", "", "network mirror base URL")
cmdFlags.StringVar(&result.TestDirectory, "test-directory", "tests", "test-directory")
cmdFlags.BoolVar(&result.EnablePluginCache, "enable-plugin-cache", false, "enable plugin cache")
if err := cmdFlags.Parse(args); err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to parse command-line flags",
err.Error(),
))
}
if remaining := cmdFlags.Args(); len(remaining) > 0 {
result.Providers = remaining
}
if result.FSMirrorDir != "" && result.NetMirrorURL != "" {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid installation method options",
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
))
}
return result, diags
}

View file

@ -0,0 +1,142 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package arguments
import (
"reflect"
"testing"
"github.com/hashicorp/terraform/internal/tfdiags"
)
func TestParseProvidersLock_valid(t *testing.T) {
testCases := map[string]struct {
args []string
want *ProvidersLock
}{
"defaults": {
nil,
&ProvidersLock{
TestDirectory: "tests",
},
},
"fs-mirror": {
[]string{"-fs-mirror=/path/to/mirror"},
&ProvidersLock{
FSMirrorDir: "/path/to/mirror",
TestDirectory: "tests",
},
},
"net-mirror": {
[]string{"-net-mirror=https://mirror.example.com/"},
&ProvidersLock{
NetMirrorURL: "https://mirror.example.com/",
TestDirectory: "tests",
},
},
"single platform": {
[]string{"-platform=linux_amd64"},
&ProvidersLock{
Platforms: FlagStringSlice{"linux_amd64"},
TestDirectory: "tests",
},
},
"multiple platforms": {
[]string{"-platform=linux_amd64", "-platform=darwin_arm64"},
&ProvidersLock{
Platforms: FlagStringSlice{"linux_amd64", "darwin_arm64"},
TestDirectory: "tests",
},
},
"enable-plugin-cache": {
[]string{"-enable-plugin-cache"},
&ProvidersLock{
EnablePluginCache: true,
TestDirectory: "tests",
},
},
"test-directory": {
[]string{"-test-directory=mytests"},
&ProvidersLock{
TestDirectory: "mytests",
},
},
"provider arguments": {
[]string{"hashicorp/aws", "hashicorp/random"},
&ProvidersLock{
Providers: []string{"hashicorp/aws", "hashicorp/random"},
TestDirectory: "tests",
},
},
"all options": {
[]string{"-fs-mirror=/mirror", "-platform=linux_amd64", "-enable-plugin-cache", "-test-directory=mytests", "hashicorp/aws"},
&ProvidersLock{
FSMirrorDir: "/mirror",
Platforms: FlagStringSlice{"linux_amd64"},
EnablePluginCache: true,
TestDirectory: "mytests",
Providers: []string{"hashicorp/aws"},
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
got, diags := ParseProvidersLock(tc.args)
if len(diags) > 0 {
t.Fatalf("unexpected diags: %v", diags)
}
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want)
}
})
}
}
func TestParseProvidersLock_invalid(t *testing.T) {
testCases := map[string]struct {
args []string
want *ProvidersLock
wantDiags tfdiags.Diagnostics
}{
"unknown flag": {
[]string{"-unknown"},
&ProvidersLock{
TestDirectory: "tests",
},
tfdiags.Diagnostics{
tfdiags.Sourceless(
tfdiags.Error,
"Failed to parse command-line flags",
"flag provided but not defined: -unknown",
),
},
},
"mirror collision": {
[]string{"-fs-mirror=/foo/", "-net-mirror=https://example.com/"},
&ProvidersLock{
FSMirrorDir: "/foo/",
NetMirrorURL: "https://example.com/",
TestDirectory: "tests",
},
tfdiags.Diagnostics{
tfdiags.Sourceless(
tfdiags.Error,
"Invalid installation method options",
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
),
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
got, gotDiags := ParseProvidersLock(tc.args)
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want)
}
tfdiags.AssertDiagnosticsMatch(t, gotDiags, tc.wantDiags)
})
}
}

View file

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/providercache"
@ -38,45 +39,33 @@ func (c *ProvidersLockCommand) Synopsis() string {
return "Write out dependency locks for the configured providers"
}
func (c *ProvidersLockCommand) Run(args []string) int {
args = c.Meta.process(args)
cmdFlags := c.Meta.defaultFlagSet("providers lock")
var optPlatforms arguments.FlagStringSlice
var fsMirrorDir string
var netMirrorURL string
var testDirectory string
func (c *ProvidersLockCommand) Run(rawArgs []string) int {
// Parse and apply global view arguments
common, rawArgs := arguments.ParseView(rawArgs)
c.View.Configure(common)
cmdFlags.Var(&optPlatforms, "platform", "target platform")
cmdFlags.StringVar(&fsMirrorDir, "fs-mirror", "", "filesystem mirror directory")
cmdFlags.StringVar(&netMirrorURL, "net-mirror", "", "network mirror base URL")
cmdFlags.StringVar(&testDirectory, "test-directory", "tests", "test-directory")
pluginCache := cmdFlags.Bool("enable-plugin-cache", false, "")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
// Propagate -no-color for legacy use of Ui
c.Meta.color = !common.NoColor
c.Meta.Color = c.Meta.color
// Parse and validate command-specific flags
args, diags := arguments.ParseProvidersLock(rawArgs)
// Instantiate the view, even if there are flag errors
view := views.NewProvidersLock(c.View)
if diags.HasErrors() {
view.Diagnostics(diags)
view.HelpPrompt()
return 1
}
var diags tfdiags.Diagnostics
if fsMirrorDir != "" && netMirrorURL != "" {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid installation method options",
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
))
c.showDiagnostics(diags)
return 1
}
providerStrs := cmdFlags.Args()
var platforms []getproviders.Platform
if len(optPlatforms) == 0 {
if len(args.Platforms) == 0 {
platforms = []getproviders.Platform{getproviders.CurrentPlatform}
} else {
platforms = make([]getproviders.Platform, 0, len(optPlatforms))
for _, platformStr := range optPlatforms {
platforms = make([]getproviders.Platform, 0, len(args.Platforms))
for _, platformStr := range args.Platforms {
platform, err := getproviders.ParsePlatform(platformStr)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
@ -104,17 +93,17 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// against the upstream checksums.
var source getproviders.Source
switch {
case fsMirrorDir != "":
source = getproviders.NewFilesystemMirrorSource(fsMirrorDir)
case netMirrorURL != "":
u, err := url.Parse(netMirrorURL)
case args.FSMirrorDir != "":
source = getproviders.NewFilesystemMirrorSource(args.FSMirrorDir)
case args.NetMirrorURL != "":
u, err := url.Parse(args.NetMirrorURL)
if err != nil || u.Scheme != "https" {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid network mirror URL",
"The -net-mirror option requires a valid https: URL as the mirror base URL.",
))
c.showDiagnostics(diags)
view.Diagnostics(diags)
return 1
}
source = getproviders.NewHTTPMirrorSource(u, c.Services.CredentialsSource())
@ -125,7 +114,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
source = getproviders.NewRegistrySource(c.Services)
}
config, confDiags := c.loadConfigWithTests(".", testDirectory)
config, confDiags := c.loadConfigWithTests(".", args.TestDirectory)
diags = diags.Append(confDiags)
reqs, hclDiags := config.ProviderRequirements()
diags = diags.Append(hclDiags)
@ -134,9 +123,9 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// we'll modify "reqs" to only include those. Modifying this is okay
// because config.ProviderRequirements generates a fresh map result
// for each call.
if len(providerStrs) != 0 {
if len(args.Providers) != 0 {
providers := map[addrs.Provider]struct{}{}
for _, raw := range providerStrs {
for _, raw := range args.Providers {
addr, moreDiags := addrs.ParseProviderSourceString(raw)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
@ -176,7 +165,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// If we have any error diagnostics already then we won't proceed further.
if diags.HasErrors() {
c.showDiagnostics(diags)
view.Diagnostics(diags)
return 1
}
@ -210,7 +199,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// Our output from this command is minimal just to show that
// we're making progress, rather than just silently hanging.
FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, loc getproviders.PackageLocation) {
c.Ui.Output(fmt.Sprintf("- Fetching %s %s for %s...", provider.ForDisplay(), version, platform))
view.Fetching(provider, version, platform)
if prevVersion, exists := selectedVersions[provider]; exists && version != prevVersion {
// This indicates a weird situation where we ended up
// selecting a different version for one platform than
@ -240,10 +229,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
if auth != nil && auth.ThirdPartySigned() {
keyID = auth.KeyID
}
if keyID != "" {
keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
}
c.Ui.Output(fmt.Sprintf("- Retrieved %s %s for %s (%s%s)", provider.ForDisplay(), version, platform, auth, keyID))
view.FetchSuccess(provider, version, platform, auth.String(), keyID)
},
}
ctx := evts.OnContext(ctx)
@ -253,7 +239,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// Use global plugin cache for extra speed if present and flag is set
globalCacheDir := c.providerGlobalCacheDir()
if *pluginCache && globalCacheDir != nil {
if args.EnablePluginCache && globalCacheDir != nil {
installer.SetGlobalCacheDir(globalCacheDir.WithPlatform(platform))
installer.SetGlobalCacheDirMayBreakDependencyLockFile(c.PluginCacheMayBreakDependencyLockFile)
}
@ -273,7 +259,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
// If we have any error diagnostics from installation then we won't
// proceed to merging and updating the lock file on disk.
if diags.HasErrors() {
c.showDiagnostics(diags)
view.Diagnostics(diags)
return 1
}
@ -318,24 +304,12 @@ func (c *ProvidersLockCommand) Run(args []string) int {
switch providersLockCalculateChangeType(oldLock, platformLock) {
case providersLockChangeTypeNewProvider:
madeAnyChange = true
c.Ui.Output(
fmt.Sprintf(
"- Obtained %s checksums for %s; This was a new provider and the checksums for this platform are now tracked in the lock file",
provider.ForDisplay(),
platform))
view.NewProvider(provider, platform)
case providersLockChangeTypeNewHashes:
madeAnyChange = true
c.Ui.Output(
fmt.Sprintf(
"- Obtained %s checksums for %s; Additional checksums for this platform are now tracked in the lock file",
provider.ForDisplay(),
platform))
view.NewHashes(provider, platform)
case providersLockChangeTypeNoChange:
c.Ui.Output(
fmt.Sprintf(
"- Obtained %s checksums for %s; All checksums for this platform were already tracked in the lock file",
provider.ForDisplay(),
platform))
view.ExistingHashes(provider, platform)
}
}
newLocks.SetProvider(provider, version, constraints, hashes)
@ -344,17 +318,12 @@ func (c *ProvidersLockCommand) Run(args []string) int {
moreDiags = c.replaceLockedDependencies(newLocks)
diags = diags.Append(moreDiags)
c.showDiagnostics(diags)
view.Diagnostics(diags)
if diags.HasErrors() {
return 1
}
if madeAnyChange {
c.Ui.Output(c.Colorize().Color("\n[bold][green]Success![reset] [bold]Terraform has updated the lock file.[reset]"))
c.Ui.Output("\nReview the changes in .terraform.lock.hcl and then commit to your\nversion control system to retain the new checksums.\n")
} else {
c.Ui.Output(c.Colorize().Color("\n[bold][green]Success![reset] [bold]Terraform has validated the lock file and found no need for changes.[reset]"))
}
view.Success(madeAnyChange)
return 0
}

View file

@ -27,9 +27,11 @@ func TestProvidersLock(t *testing.T) {
t.Chdir(td)
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ProvidersLockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
code := c.Run([]string{})
@ -119,9 +121,11 @@ func runProviderLockGenericTest(t *testing.T, testDirectory, expected string, in
p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ProvidersLockCommand{
Meta: Meta{
Ui: ui,
View: view,
testingOverrides: metaOverridesForProvider(p),
},
}
@ -146,9 +150,11 @@ func TestProvidersLock_args(t *testing.T) {
t.Run("mirror collision", func(t *testing.T) {
ui := new(cli.MockUi)
view, done := testView(t)
c := &ProvidersLockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -162,7 +168,7 @@ func TestProvidersLock_args(t *testing.T) {
if code != 1 {
t.Fatalf("wrong exit code; expected 1, got %d", code)
}
output := ui.ErrorWriter.String()
output := done(t).Stderr()
if !strings.Contains(output, "The -fs-mirror and -net-mirror command line options are mutually-exclusive.") {
t.Fatalf("missing expected error message: %s", output)
}
@ -170,9 +176,11 @@ func TestProvidersLock_args(t *testing.T) {
t.Run("invalid platform", func(t *testing.T) {
ui := new(cli.MockUi)
view, done := testView(t)
c := &ProvidersLockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -183,17 +191,19 @@ func TestProvidersLock_args(t *testing.T) {
if code != 1 {
t.Fatalf("wrong exit code; expected 1, got %d", code)
}
output := ui.ErrorWriter.String()
if !strings.Contains(output, "must be two words separated by an underscore.") {
output := done(t).Stderr()
if !strings.Contains(output, "not a valid target platform") {
t.Fatalf("missing expected error message: %s", output)
}
})
t.Run("invalid provider argument", func(t *testing.T) {
ui := new(cli.MockUi)
view, done := testView(t)
c := &ProvidersLockCommand{
Meta: Meta{
Ui: ui,
Ui: ui,
View: view,
},
}
@ -204,8 +214,8 @@ func TestProvidersLock_args(t *testing.T) {
if code != 1 {
t.Fatalf("wrong exit code; expected 1, got %d", code)
}
output := ui.ErrorWriter.String()
if !strings.Contains(output, "The provider registry.terraform.io/hashicorp/random is not required by the\ncurrent configuration.") {
output := done(t).Stderr()
if !strings.Contains(output, "is not required by") {
t.Fatalf("missing expected error message: %s", output)
}
})

View file

@ -0,0 +1,100 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package views
import (
"fmt"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// ProvidersLock is the view interface for the providers lock command.
type ProvidersLock interface {
// Fetching announces that a provider package is being fetched.
Fetching(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform)
// FetchSuccess announces that a provider package was fetched successfully.
FetchSuccess(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform, auth string, keyID string)
// NewProvider announces that checksums for a new provider have been added to the lock file.
NewProvider(provider addrs.Provider, platform getproviders.Platform)
// NewHashes announces that additional checksums have been added for an existing provider.
NewHashes(provider addrs.Provider, platform getproviders.Platform)
// ExistingHashes announces that all checksums were already tracked.
ExistingHashes(provider addrs.Provider, platform getproviders.Platform)
// Success announces a successful completion, with whether any changes were made.
Success(madeChanges bool)
// Diagnostics renders diagnostics.
Diagnostics(diags tfdiags.Diagnostics)
// HelpPrompt directs the user to the help output.
HelpPrompt()
}
// NewProvidersLock returns an initialized ProvidersLock implementation.
func NewProvidersLock(view *View) ProvidersLock {
return &ProvidersLockHuman{view: view}
}
// ProvidersLockHuman is the human-readable implementation of the ProvidersLock view.
type ProvidersLockHuman struct {
view *View
}
var _ ProvidersLock = (*ProvidersLockHuman)(nil)
func (v *ProvidersLockHuman) Fetching(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform) {
v.view.streams.Println(fmt.Sprintf("- Fetching %s %s for %s...", provider.ForDisplay(), version, platform))
}
func (v *ProvidersLockHuman) FetchSuccess(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform, auth string, keyID string) {
if keyID != "" {
keyID = v.view.colorize.Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
}
v.view.streams.Println(fmt.Sprintf("- Retrieved %s %s for %s (%s%s)", provider.ForDisplay(), version, platform, auth, keyID))
}
func (v *ProvidersLockHuman) NewProvider(provider addrs.Provider, platform getproviders.Platform) {
v.view.streams.Println(fmt.Sprintf(
"- Obtained %s checksums for %s; This was a new provider and the checksums for this platform are now tracked in the lock file",
provider.ForDisplay(),
platform))
}
func (v *ProvidersLockHuman) NewHashes(provider addrs.Provider, platform getproviders.Platform) {
v.view.streams.Println(fmt.Sprintf(
"- Obtained %s checksums for %s; Additional checksums for this platform are now tracked in the lock file",
provider.ForDisplay(),
platform))
}
func (v *ProvidersLockHuman) ExistingHashes(provider addrs.Provider, platform getproviders.Platform) {
v.view.streams.Println(fmt.Sprintf(
"- Obtained %s checksums for %s; All checksums for this platform were already tracked in the lock file",
provider.ForDisplay(),
platform))
}
func (v *ProvidersLockHuman) Success(madeChanges bool) {
if madeChanges {
v.view.streams.Println(v.view.colorize.Color("\n[bold][green]Success![reset] [bold]Terraform has updated the lock file.[reset]"))
v.view.streams.Println("\nReview the changes in .terraform.lock.hcl and then commit to your\nversion control system to retain the new checksums.")
} else {
v.view.streams.Println(v.view.colorize.Color("\n[bold][green]Success![reset] [bold]Terraform has validated the lock file and found no need for changes.[reset]"))
}
}
func (v *ProvidersLockHuman) Diagnostics(diags tfdiags.Diagnostics) {
v.view.Diagnostics(diags)
}
func (v *ProvidersLockHuman) HelpPrompt() {
v.view.HelpPrompt("providers lock")
}

View file

@ -0,0 +1,141 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package views
import (
"strings"
"testing"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/terminal"
)
func TestProvidersLockHuman_Fetching(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
provider := addrs.NewDefaultProvider("test")
version := getproviders.MustParseVersion("1.0.0")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.Fetching(provider, version, platform)
got := done(t).Stdout()
want := "- Fetching hashicorp/test 1.0.0 for linux_amd64...\n"
if got != want {
t.Fatalf("wrong output\n got: %q\nwant: %q", got, want)
}
}
func TestProvidersLockHuman_FetchSuccess(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
provider := addrs.NewDefaultProvider("test")
version := getproviders.MustParseVersion("1.0.0")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.FetchSuccess(provider, version, platform, "signed", "")
got := done(t).Stdout()
want := "- Retrieved hashicorp/test 1.0.0 for linux_amd64 (signed)\n"
if got != want {
t.Fatalf("wrong output\n got: %q\nwant: %q", got, want)
}
}
func TestProvidersLockHuman_FetchSuccess_withKeyID(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
provider := addrs.NewDefaultProvider("test")
version := getproviders.MustParseVersion("1.0.0")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.FetchSuccess(provider, version, platform, "signed", "ABC123")
got := done(t).Stdout()
if !strings.Contains(got, "ABC123") {
t.Fatalf("expected output to contain key ID, got: %q", got)
}
}
func TestProvidersLockHuman_NewProvider(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
provider := addrs.NewDefaultProvider("test")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.NewProvider(provider, platform)
got := done(t).Stdout()
if !strings.Contains(got, "new provider") {
t.Fatalf("expected output to mention new provider, got: %q", got)
}
}
func TestProvidersLockHuman_NewHashes(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
provider := addrs.NewDefaultProvider("test")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.NewHashes(provider, platform)
got := done(t).Stdout()
if !strings.Contains(got, "Additional checksums") {
t.Fatalf("expected output to mention additional checksums, got: %q", got)
}
}
func TestProvidersLockHuman_ExistingHashes(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
provider := addrs.NewDefaultProvider("test")
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
v.ExistingHashes(provider, platform)
got := done(t).Stdout()
if !strings.Contains(got, "already tracked") {
t.Fatalf("expected output to mention already tracked, got: %q", got)
}
}
func TestProvidersLockHuman_Success_withChanges(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
v.Success(true)
got := done(t).Stdout()
if !strings.Contains(got, "updated the lock file") {
t.Fatalf("expected output to mention updated lock file, got: %q", got)
}
if !strings.Contains(got, "Review the changes") {
t.Fatalf("expected output to mention reviewing changes, got: %q", got)
}
}
func TestProvidersLockHuman_Success_noChanges(t *testing.T) {
streams, done := terminal.StreamsForTesting(t)
v := NewProvidersLock(NewView(streams))
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
v.Success(false)
got := done(t).Stdout()
if !strings.Contains(got, "no need for changes") {
t.Fatalf("expected output to mention no need for changes, got: %q", got)
}
}