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 }