github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/backend/remote-state/http/client_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package http
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"net/url"
    13  	"reflect"
    14  	"testing"
    15  
    16  	"github.com/hashicorp/go-retryablehttp"
    17  	"github.com/terramate-io/tf/states/remote"
    18  )
    19  
    20  func TestHTTPClient_impl(t *testing.T) {
    21  	var _ remote.Client = new(httpClient)
    22  	var _ remote.ClientLocker = new(httpClient)
    23  }
    24  
    25  func TestHTTPClient(t *testing.T) {
    26  	handler := new(testHTTPHandler)
    27  	ts := httptest.NewServer(http.HandlerFunc(handler.Handle))
    28  	defer ts.Close()
    29  
    30  	url, err := url.Parse(ts.URL)
    31  	if err != nil {
    32  		t.Fatalf("Parse: %s", err)
    33  	}
    34  
    35  	// Test basic get/update
    36  	client := &httpClient{URL: url, Client: retryablehttp.NewClient()}
    37  	remote.TestClient(t, client)
    38  
    39  	// test just a single PUT
    40  	p := &httpClient{
    41  		URL:          url,
    42  		UpdateMethod: "PUT",
    43  		Client:       retryablehttp.NewClient(),
    44  	}
    45  	remote.TestClient(t, p)
    46  
    47  	// Test locking and alternative UpdateMethod
    48  	a := &httpClient{
    49  		URL:          url,
    50  		UpdateMethod: "PUT",
    51  		LockURL:      url,
    52  		LockMethod:   "LOCK",
    53  		UnlockURL:    url,
    54  		UnlockMethod: "UNLOCK",
    55  		Client:       retryablehttp.NewClient(),
    56  	}
    57  	b := &httpClient{
    58  		URL:          url,
    59  		UpdateMethod: "PUT",
    60  		LockURL:      url,
    61  		LockMethod:   "LOCK",
    62  		UnlockURL:    url,
    63  		UnlockMethod: "UNLOCK",
    64  		Client:       retryablehttp.NewClient(),
    65  	}
    66  	remote.TestRemoteLocks(t, a, b)
    67  
    68  	// test a WebDAV-ish backend
    69  	davhandler := new(testHTTPHandler)
    70  	ts = httptest.NewServer(http.HandlerFunc(davhandler.HandleWebDAV))
    71  	defer ts.Close()
    72  
    73  	url, err = url.Parse(ts.URL)
    74  	client = &httpClient{
    75  		URL:          url,
    76  		UpdateMethod: "PUT",
    77  		Client:       retryablehttp.NewClient(),
    78  	}
    79  	if err != nil {
    80  		t.Fatalf("Parse: %s", err)
    81  	}
    82  
    83  	remote.TestClient(t, client) // first time through: 201
    84  	remote.TestClient(t, client) // second time, with identical data: 204
    85  
    86  	// test a broken backend
    87  	brokenHandler := new(testBrokenHTTPHandler)
    88  	brokenHandler.handler = new(testHTTPHandler)
    89  	ts = httptest.NewServer(http.HandlerFunc(brokenHandler.Handle))
    90  	defer ts.Close()
    91  
    92  	url, err = url.Parse(ts.URL)
    93  	if err != nil {
    94  		t.Fatalf("Parse: %s", err)
    95  	}
    96  	client = &httpClient{URL: url, Client: retryablehttp.NewClient()}
    97  	remote.TestClient(t, client)
    98  }
    99  
   100  type testHTTPHandler struct {
   101  	Data   []byte
   102  	Locked bool
   103  }
   104  
   105  func (h *testHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
   106  	switch r.Method {
   107  	case "GET":
   108  		w.Write(h.Data)
   109  	case "PUT":
   110  		buf := new(bytes.Buffer)
   111  		if _, err := io.Copy(buf, r.Body); err != nil {
   112  			w.WriteHeader(500)
   113  		}
   114  		w.WriteHeader(201)
   115  		h.Data = buf.Bytes()
   116  	case "POST":
   117  		buf := new(bytes.Buffer)
   118  		if _, err := io.Copy(buf, r.Body); err != nil {
   119  			w.WriteHeader(500)
   120  		}
   121  		h.Data = buf.Bytes()
   122  	case "LOCK":
   123  		if h.Locked {
   124  			w.WriteHeader(423)
   125  		} else {
   126  			h.Locked = true
   127  		}
   128  	case "UNLOCK":
   129  		h.Locked = false
   130  	case "DELETE":
   131  		h.Data = nil
   132  		w.WriteHeader(200)
   133  	default:
   134  		w.WriteHeader(500)
   135  		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
   136  	}
   137  }
   138  
   139  // mod_dav-ish behavior
   140  func (h *testHTTPHandler) HandleWebDAV(w http.ResponseWriter, r *http.Request) {
   141  	switch r.Method {
   142  	case "GET":
   143  		w.Write(h.Data)
   144  	case "PUT":
   145  		buf := new(bytes.Buffer)
   146  		if _, err := io.Copy(buf, r.Body); err != nil {
   147  			w.WriteHeader(500)
   148  		}
   149  		if reflect.DeepEqual(h.Data, buf.Bytes()) {
   150  			h.Data = buf.Bytes()
   151  			w.WriteHeader(204)
   152  		} else {
   153  			h.Data = buf.Bytes()
   154  			w.WriteHeader(201)
   155  		}
   156  	case "DELETE":
   157  		h.Data = nil
   158  		w.WriteHeader(200)
   159  	default:
   160  		w.WriteHeader(500)
   161  		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
   162  	}
   163  }
   164  
   165  type testBrokenHTTPHandler struct {
   166  	lastRequestWasBroken bool
   167  	handler              *testHTTPHandler
   168  }
   169  
   170  func (h *testBrokenHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
   171  	if h.lastRequestWasBroken {
   172  		h.lastRequestWasBroken = false
   173  		h.handler.Handle(w, r)
   174  	} else {
   175  		h.lastRequestWasBroken = true
   176  		w.WriteHeader(500)
   177  	}
   178  }