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  }