// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package arguments import ( "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/opentofu/opentofu/internal/command/flags" "github.com/opentofu/opentofu/internal/command/workdir" ) func TestParseImport_basicValidation(t *testing.T) { wd := workdir.NewDir(".") testCases := map[string]struct { args []string want *Import wantErrText string }{ "defaults": { args: []string{"addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" }), }, "parallelism flag": { args: []string{"-parallelism=5", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.Parallelism = 5 }), }, "config flag": { args: []string{"-config=/path/to/config", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.ConfigPath = "/path/to/config" }), }, "ignore-remote-version flag": { args: []string{"-ignore-remote-version", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.Backend.IgnoreRemoteVersion = true }), }, "state flags": { args: []string{ "-lock=false", "-lock-timeout=10s", "-state=foo.tfstate", "-state-out=bar.tfstate", "-backup=baz.tfstate", "addr", "id", }, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.State.Lock = false imp.State.LockTimeout = 10 * time.Second imp.State.StatePath = "foo.tfstate" imp.State.StateOutPath = "bar.tfstate" imp.State.BackupPath = "baz.tfstate" }), }, "var flags": { args: []string{"-var=foo=bar", "-var-file=vars.tfvars", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" }), }, "input flag": { args: []string{"-input=false", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.ViewOptions.InputEnabled = false }), }, "json flag": { args: []string{"-json", "addr", "id"}, want: importArgsWithDefaults(func(imp *Import) { imp.ResourceAddress = "addr" imp.ResourceID = "id" imp.ViewOptions.ViewType = ViewJSON imp.ViewOptions.InputEnabled = false }), }, "no arguments": { args: []string{}, want: importArgsWithDefaults(nil), wantErrText: "Invalid number of arguments: The import command expects two arguments", }, "one argument": { args: []string{"addr"}, want: importArgsWithDefaults(nil), wantErrText: "Invalid number of arguments: The import command expects two arguments", }, "too many arguments": { args: []string{"addr", "id", "extra"}, want: importArgsWithDefaults(nil), wantErrText: "Invalid number of arguments: The import command expects two arguments", }, } cmpOpts := cmpopts.IgnoreUnexported(Vars{}, ViewOptions{}) for name, tc := range testCases { t.Run(name, func(t *testing.T) { got, closer, diags := ParseImport(tc.args, wd) defer closer() if tc.wantErrText != "" && len(diags) == 0 { t.Errorf("test wanted error but got nothing") } else if tc.wantErrText == "" && len(diags) > 0 { t.Errorf("test didn't expect errors but got some: %s", diags.ErrWithWarnings()) } else if tc.wantErrText != "" && len(diags) > 0 { errStr := diags.ErrWithWarnings().Error() if !strings.Contains(errStr, tc.wantErrText) { t.Errorf("the returned diagnostics does not contain the expected error message.\ndiags:\n%s\nwanted: %s\n", errStr, tc.wantErrText) } } if diff := cmp.Diff(tc.want, got, cmpOpts); diff != "" { t.Errorf("unexpected result\n%s", diff) } }) } } func TestParseImport_vars(t *testing.T) { wd := workdir.NewDir(".") testCases := map[string]struct { args []string wantCount int wantEmpty bool }{ "no vars": { args: []string{"addr", "id"}, wantCount: 0, wantEmpty: true, }, "single var": { args: []string{"-var", "foo=bar", "addr", "id"}, wantCount: 1, wantEmpty: false, }, "single var-file": { args: []string{"-var-file", "terraform.tfvars", "addr", "id"}, wantCount: 1, wantEmpty: false, }, "multiple vars mixed": { args: []string{"-var", "a=1", "-var-file", "f.tfvars", "-var", "b=2", "addr", "id"}, wantCount: 3, wantEmpty: false, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { got, closer, diags := ParseImport(tc.args, wd) defer closer() if len(diags) > 0 { t.Fatalf("unexpected diags: %v", diags) } if got.Vars.Empty() != tc.wantEmpty { t.Errorf("Vars.Empty() = %v, want %v", got.Vars.Empty(), tc.wantEmpty) } if len(got.Vars.All()) != tc.wantCount { t.Errorf("len(Vars.All()) = %d, want %d", len(got.Vars.All()), tc.wantCount) } }) } } func importArgsWithDefaults(mutate func(imp *Import)) *Import { v := flags.NewRawFlags("-var") vf := flags.NewRawFlags("-var-file") ret := &Import{ ResourceAddress: "", ResourceID: "", ConfigPath: ".", Parallelism: DefaultParallelism, ViewOptions: ViewOptions{ ViewType: ViewHuman, InputEnabled: true, }, Vars: &Vars{ vars: &v, varFiles: &vf, }, State: &State{ Lock: true, StatePath: "", }, Backend: &Backend{}, } if mutate != nil { mutate(ret) } return ret }