github.com/opentofu/opentofu@v1.7.1/internal/states/statefile/roundtrip_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 statefile 7 8 import ( 9 "bytes" 10 "os" 11 "path/filepath" 12 "sort" 13 "strings" 14 "testing" 15 16 "github.com/go-test/deep" 17 "github.com/opentofu/opentofu/internal/encryption" 18 "github.com/opentofu/opentofu/internal/encryption/enctest" 19 ) 20 21 func TestRoundtrip(t *testing.T) { 22 const dir = "testdata/roundtrip" 23 entries, err := os.ReadDir(dir) 24 if err != nil { 25 t.Fatal(err) 26 } 27 28 for _, info := range entries { 29 const inSuffix = ".in.tfstate" 30 const outSuffix = ".out.tfstate" 31 32 if info.IsDir() { 33 continue 34 } 35 inName := info.Name() 36 if !strings.HasSuffix(inName, inSuffix) { 37 continue 38 } 39 name := inName[:len(inName)-len(inSuffix)] 40 outName := name + outSuffix 41 42 t.Run(name, func(t *testing.T) { 43 oSrcWant, err := os.ReadFile(filepath.Join(dir, outName)) 44 if err != nil { 45 t.Fatal(err) 46 } 47 oWant, diags := readStateV4(oSrcWant) 48 if diags.HasErrors() { 49 t.Fatal(diags.Err()) 50 } 51 52 ir, err := os.Open(filepath.Join(dir, inName)) 53 if err != nil { 54 t.Fatal(err) 55 } 56 defer ir.Close() 57 58 f, err := Read(ir, encryption.StateEncryptionDisabled()) 59 if err != nil { 60 t.Fatalf("unexpected error: %s", err) 61 } 62 63 var buf bytes.Buffer 64 err = Write(f, &buf, encryption.StateEncryptionDisabled()) 65 if err != nil { 66 t.Fatal(err) 67 } 68 oSrcWritten := buf.Bytes() 69 70 oGot, diags := readStateV4(oSrcWritten) 71 if diags.HasErrors() { 72 t.Fatal(diags.Err()) 73 } 74 75 problems := deep.Equal(oGot, oWant) 76 sort.Strings(problems) 77 for _, problem := range problems { 78 t.Error(problem) 79 } 80 }) 81 } 82 } 83 84 func TestRoundtripEncryption(t *testing.T) { 85 const path = "testdata/roundtrip/v4-modules.out.tfstate" 86 87 enc := enctest.EncryptionWithFallback().State() 88 89 unencryptedInput, err := os.Open(path) 90 if err != nil { 91 t.Fatal(err) 92 } 93 defer unencryptedInput.Close() 94 95 // Read unencrypted using fallback 96 originalState, err := Read(unencryptedInput, enc) 97 if err != nil { 98 t.Fatalf("unexpected error: %s", err) 99 } 100 101 // Write encrypted 102 var encrypted bytes.Buffer 103 err = Write(originalState, &encrypted, enc) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 // Make sure it is encrypted / not readable 109 encryptedCopy := encrypted 110 _, err = Read(&encryptedCopy, encryption.StateEncryptionDisabled()) 111 if err == nil || err.Error() != "Unsupported state file format: This state file is encrypted and can not be read without an encryption configuration" { 112 t.Fatalf("expected written state file to be encrypted!") 113 } 114 115 // Read encrypted 116 newState, err := Read(&encrypted, enc) 117 if err != nil { 118 t.Fatalf("unexpected error: %s", err) 119 } 120 121 // Compare before/after encryption workflow 122 problems := deep.Equal(newState, originalState) 123 sort.Strings(problems) 124 for _, problem := range problems { 125 t.Error(problem) 126 } 127 }