github.com/opentofu/opentofu@v1.7.1/internal/states/remote/remote_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 remote 7 8 import ( 9 "crypto/md5" 10 "encoding/json" 11 "testing" 12 ) 13 14 func TestRemoteClient_noPayload(t *testing.T) { 15 s := &State{ 16 Client: nilClient{}, 17 } 18 if err := s.RefreshState(); err != nil { 19 t.Fatal("error refreshing empty remote state") 20 } 21 } 22 23 // nilClient returns nil for everything 24 type nilClient struct{} 25 26 func (nilClient) Get() (*Payload, error) { return nil, nil } 27 28 func (c nilClient) Put([]byte) error { return nil } 29 30 func (c nilClient) Delete() error { return nil } 31 32 // mockClient is a client that tracks persisted state snapshots only in 33 // memory and also logs what it has been asked to do for use in test 34 // assertions. 35 type mockClient struct { 36 current []byte 37 log []mockClientRequest 38 } 39 40 type mockClientRequest struct { 41 Method string 42 Content map[string]interface{} 43 } 44 45 func (c *mockClient) Get() (*Payload, error) { 46 c.appendLog("Get", c.current) 47 if c.current == nil { 48 return nil, nil 49 } 50 checksum := md5.Sum(c.current) 51 return &Payload{ 52 Data: c.current, 53 MD5: checksum[:], 54 }, nil 55 } 56 57 func (c *mockClient) Put(data []byte) error { 58 c.appendLog("Put", data) 59 c.current = data 60 return nil 61 } 62 63 func (c *mockClient) Delete() error { 64 c.appendLog("Delete", c.current) 65 c.current = nil 66 return nil 67 } 68 69 func (c *mockClient) appendLog(method string, content []byte) { 70 // For easier test assertions, we actually log the result of decoding 71 // the content JSON rather than the raw bytes. Callers are in principle 72 // allowed to provide any arbitrary bytes here, but we know we're only 73 // using this to test our own State implementation here and that always 74 // uses the JSON state format, so this is fine. 75 76 var contentVal map[string]interface{} 77 if content != nil { 78 err := json.Unmarshal(content, &contentVal) 79 if err != nil { 80 panic(err) // should never happen because our tests control this input 81 } 82 } 83 c.log = append(c.log, mockClientRequest{method, contentVal}) 84 } 85 86 // mockClientForcePusher is like mockClient, but also implements 87 // EnableForcePush, allowing testing for this behavior 88 type mockClientForcePusher struct { 89 current []byte 90 force bool 91 log []mockClientRequest 92 } 93 94 func (c *mockClientForcePusher) Get() (*Payload, error) { 95 c.appendLog("Get", c.current) 96 if c.current == nil { 97 return nil, nil 98 } 99 checksum := md5.Sum(c.current) 100 return &Payload{ 101 Data: c.current, 102 MD5: checksum[:], 103 }, nil 104 } 105 106 func (c *mockClientForcePusher) Put(data []byte) error { 107 if c.force { 108 c.appendLog("Force Put", data) 109 } else { 110 c.appendLog("Put", data) 111 } 112 c.current = data 113 return nil 114 } 115 116 // Implements remote.ClientForcePusher 117 func (c *mockClientForcePusher) EnableForcePush() { 118 c.force = true 119 } 120 121 func (c *mockClientForcePusher) Delete() error { 122 c.appendLog("Delete", c.current) 123 c.current = nil 124 return nil 125 } 126 func (c *mockClientForcePusher) appendLog(method string, content []byte) { 127 var contentVal map[string]interface{} 128 if content != nil { 129 err := json.Unmarshal(content, &contentVal) 130 if err != nil { 131 panic(err) // should never happen because our tests control this input 132 } 133 } 134 c.log = append(c.log, mockClientRequest{method, contentVal}) 135 }