@@ -20,6 +20,7 @@ import (
2020 "github.com/zclconf/go-cty/cty"
2121
2222 "github.com/hashicorp/terraform/internal/addrs"
23+ "github.com/hashicorp/terraform/internal/backend"
2324 "github.com/hashicorp/terraform/internal/command/arguments"
2425 "github.com/hashicorp/terraform/internal/command/views"
2526 "github.com/hashicorp/terraform/internal/configs"
@@ -3227,6 +3228,111 @@ func TestInit_testsWithModule(t *testing.T) {
32273228 }
32283229}
32293230
3231+ // Testing init's behaviors when run in an empty working directory
3232+ func TestInit_stateStore_newWorkingDir (t * testing.T ) {
3233+ t .Run ("an init command creates the default workspace by default" , func (t * testing.T ) {
3234+ // Create a temporary, uninitialized working directory with configuration including a state store
3235+ td := t .TempDir ()
3236+ testCopyDir (t , testFixturePath ("init-with-state-store" ), td )
3237+ t .Chdir (td )
3238+
3239+ mockProvider := mockPluggableStateStorageProvider ()
3240+ mockProviderAddress := addrs .NewDefaultProvider ("test" )
3241+ providerSource , close := newMockProviderSource (t , map [string ][]string {
3242+ "hashicorp/test" : {"1.0.0" },
3243+ })
3244+ defer close ()
3245+
3246+ ui := new (cli.MockUi )
3247+ view , done := testView (t )
3248+ c := & InitCommand {
3249+ Meta : Meta {
3250+ Ui : ui ,
3251+ View : view ,
3252+ AllowExperimentalFeatures : true ,
3253+ testingOverrides : & testingOverrides {
3254+ Providers : map [addrs.Provider ]providers.Factory {
3255+ mockProviderAddress : providers .FactoryFixed (mockProvider ),
3256+ },
3257+ },
3258+ ProviderSource : providerSource ,
3259+ },
3260+ }
3261+
3262+ args := []string {"-enable-pluggable-state-storage-experiment=true" }
3263+ code := c .Run (args )
3264+ testOutput := done (t )
3265+ if code != 0 {
3266+ t .Fatalf ("expected code 0 exit code, got %d, output: \n %s" , code , testOutput .All ())
3267+ }
3268+
3269+ // Check output
3270+ output := testOutput .All ()
3271+ expectedOutput := `Initializing the state store...`
3272+ if ! strings .Contains (output , expectedOutput ) {
3273+ t .Fatalf ("expected output to include %q, but got':\n %s" , expectedOutput , output )
3274+ }
3275+
3276+ // Assert the default workspace was created
3277+ if _ , exists := mockProvider .MockStates [backend .DefaultStateName ]; ! exists {
3278+ t .Fatal ("expected the default workspace to be created during init, but it is missing" )
3279+ }
3280+ })
3281+
3282+ t .Run ("an init command with the flag -create-default-workspace=false will not make the default workspace" , func (t * testing.T ) {
3283+ // Create a temporary, uninitialized working directory with configuration including a state store
3284+ td := t .TempDir ()
3285+ testCopyDir (t , testFixturePath ("init-with-state-store" ), td )
3286+ t .Chdir (td )
3287+
3288+ mockProvider := mockPluggableStateStorageProvider ()
3289+ mockProviderAddress := addrs .NewDefaultProvider ("test" )
3290+ providerSource , close := newMockProviderSource (t , map [string ][]string {
3291+ "hashicorp/test" : {"1.0.0" },
3292+ })
3293+ defer close ()
3294+
3295+ ui := new (cli.MockUi )
3296+ view , done := testView (t )
3297+ c := & InitCommand {
3298+ Meta : Meta {
3299+ Ui : ui ,
3300+ View : view ,
3301+ AllowExperimentalFeatures : true ,
3302+ testingOverrides : & testingOverrides {
3303+ Providers : map [addrs.Provider ]providers.Factory {
3304+ mockProviderAddress : providers .FactoryFixed (mockProvider ),
3305+ },
3306+ },
3307+ ProviderSource : providerSource ,
3308+ },
3309+ }
3310+
3311+ args := []string {"-enable-pluggable-state-storage-experiment=true" , "-create-default-workspace=false" }
3312+ code := c .Run (args )
3313+ testOutput := done (t )
3314+ if code != 0 {
3315+ t .Fatalf ("expected code 0 exit code, got %d, output: \n %s" , code , testOutput .All ())
3316+ }
3317+
3318+ // Check output
3319+ output := testOutput .All ()
3320+ expectedOutput := `Initializing the state store...`
3321+ if ! strings .Contains (output , expectedOutput ) {
3322+ t .Fatalf ("expected output to include %q, but got':\n %s" , expectedOutput , output )
3323+ }
3324+
3325+ // Assert the default workspace was created
3326+ if _ , exists := mockProvider .MockStates [backend .DefaultStateName ]; exists {
3327+ t .Fatal ("expected Terraform to skip creating the default workspace, but it has been created" )
3328+ }
3329+ })
3330+
3331+ // TODO: Add test cases below once PSS feature isn't experimental.
3332+ // Currently these tests are handled at a lower level in `internal/command/meta_backend_test.go`:
3333+ // > "during a non-init command, the command ends in with an error telling the user to run an init command"
3334+ }
3335+
32303336// newMockProviderSource is a helper to succinctly construct a mock provider
32313337// source that contains a set of packages matching the given provider versions
32323338// that are available for installation (from temporary local files).
@@ -3367,3 +3473,65 @@ func expectedPackageInstallPath(name, version string, exe bool) string {
33673473 baseDir , fmt .Sprintf ("registry.terraform.io/hashicorp/%s/%s/%s" , name , version , platform ),
33683474 ))
33693475}
3476+
3477+ func mockPluggableStateStorageProvider () * testing_provider.MockProvider {
3478+ // Create a mock provider to use for PSS
3479+ // Get mock provider factory to be used during init
3480+ //
3481+ // This imagines a provider called `test` that contains
3482+ // a pluggable state store implementation called `store`.
3483+ pssName := "test_store"
3484+ mock := testing_provider.MockProvider {
3485+ GetProviderSchemaResponse : & providers.GetProviderSchemaResponse {
3486+ Provider : providers.Schema {
3487+ Body : & configschema.Block {
3488+ Attributes : map [string ]* configschema.Attribute {
3489+ "region" : {Type : cty .String , Optional : true },
3490+ },
3491+ },
3492+ },
3493+ DataSources : map [string ]providers.Schema {},
3494+ ResourceTypes : map [string ]providers.Schema {},
3495+ ListResourceTypes : map [string ]providers.Schema {},
3496+ StateStores : map [string ]providers.Schema {
3497+ pssName : {
3498+ Body : & configschema.Block {
3499+ Attributes : map [string ]* configschema.Attribute {
3500+ "bar" : {
3501+ Type : cty .String ,
3502+ Required : true ,
3503+ },
3504+ },
3505+ },
3506+ },
3507+ },
3508+ },
3509+ }
3510+ mock .WriteStateBytesFn = func (req providers.WriteStateBytesRequest ) providers.WriteStateBytesResponse {
3511+ // Workspaces exist once the artefact representing it is written
3512+ if _ , exist := mock .MockStates [req .StateId ]; ! exist {
3513+ // Ensure non-nil map
3514+ if mock .MockStates == nil {
3515+ mock .MockStates = make (map [string ]interface {})
3516+ }
3517+
3518+ mock .MockStates [req .StateId ] = req .Bytes
3519+ }
3520+ return providers.WriteStateBytesResponse {
3521+ Diagnostics : nil , // success
3522+ }
3523+ }
3524+ mock .ReadStateBytesFn = func (req providers.ReadStateBytesRequest ) providers.ReadStateBytesResponse {
3525+ state := []byte {}
3526+ if v , exist := mock .MockStates [req .StateId ]; exist {
3527+ if s , ok := v .([]byte ); ok {
3528+ state = s
3529+ }
3530+ }
3531+ return providers.ReadStateBytesResponse {
3532+ Bytes : state ,
3533+ Diagnostics : nil , // success
3534+ }
3535+ }
3536+ return & mock
3537+ }
0 commit comments