istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/request/command_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package request 16 17 import ( 18 "fmt" 19 "io" 20 "net/http" 21 "net/http/httptest" 22 "net/url" 23 "sync" 24 "testing" 25 26 "istio.io/istio/pkg/ptr" 27 "istio.io/istio/tests/util" 28 ) 29 30 type pilotStubHandler struct { 31 sync.Mutex 32 States []pilotStubState 33 } 34 35 type pilotStubState struct { 36 wantMethod string 37 wantPath string 38 wantBody []byte 39 StatusCode int 40 Response string 41 } 42 43 func (p *pilotStubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 44 p.Lock() 45 if r.Method == p.States[0].wantMethod { 46 if r.URL.Path == p.States[0].wantPath { 47 defer r.Body.Close() 48 body, _ := io.ReadAll(r.Body) 49 if err := util.Compare(body, p.States[0].wantBody); err == nil { 50 w.WriteHeader(p.States[0].StatusCode) 51 w.Write([]byte(p.States[0].Response)) 52 } else { 53 w.WriteHeader(http.StatusBadRequest) 54 w.Write([]byte(fmt.Sprintf("wanted body %q got %q", string(p.States[0].wantBody), string(body)))) 55 } 56 } else { 57 w.WriteHeader(http.StatusBadRequest) 58 w.Write([]byte(fmt.Sprintf("wanted path %q got %q", p.States[0].wantPath, r.URL.Path))) 59 } 60 } else { 61 w.WriteHeader(http.StatusBadRequest) 62 w.Write([]byte(fmt.Sprintf("wanted method %q got %q", p.States[0].wantMethod, r.Method))) 63 } 64 65 p.States[0] = ptr.Empty[pilotStubState]() 66 p.States = p.States[1:] 67 p.Unlock() 68 } 69 70 func Test_command_do(t *testing.T) { 71 tests := []struct { 72 name string 73 method string 74 path string 75 body string 76 pilotStates []pilotStubState 77 pilotNotReachable bool 78 wantError bool 79 }{ 80 { 81 name: "makes a request using passed method, url and body", 82 method: "POST", 83 path: "/want/path", 84 body: "body", 85 pilotStates: []pilotStubState{ 86 {StatusCode: 200, Response: "fine", wantMethod: "POST", wantPath: "/want/path", wantBody: []byte("body")}, 87 }, 88 }, 89 { 90 name: "adds / prefix to path if required", 91 method: "POST", 92 path: "want/path", 93 body: "body", 94 pilotStates: []pilotStubState{ 95 {StatusCode: 200, Response: "fine", wantMethod: "POST", wantPath: "/want/path", wantBody: []byte("body")}, 96 }, 97 }, 98 { 99 name: "handles empty string body in args", 100 method: "GET", 101 path: "/want/path", 102 body: "", 103 pilotStates: []pilotStubState{ 104 {StatusCode: 200, Response: "fine", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, 105 }, 106 }, 107 { 108 name: "doesn't error on 404", 109 method: "GET", 110 path: "/want/path", 111 body: "", 112 pilotStates: []pilotStubState{ 113 {StatusCode: 404, Response: "not-found", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, 114 }, 115 }, 116 { 117 name: "errors if Pilot is unreachable", 118 method: "GET", 119 path: "/want/path", 120 pilotNotReachable: true, 121 wantError: true, 122 }, 123 { 124 name: "errors if Pilot responds with a non success status", 125 method: "GET", 126 path: "/not/wanted/path", 127 body: "", 128 pilotStates: []pilotStubState{ 129 {StatusCode: 200, Response: "fine", wantMethod: "GET", wantPath: "/want/path", wantBody: nil}, 130 }, 131 wantError: true, 132 }, 133 } 134 for _, tt := range tests { 135 t.Run(tt.name, func(t *testing.T) { 136 pilotStub := httptest.NewServer( 137 &pilotStubHandler{States: tt.pilotStates}, 138 ) 139 stubURL, _ := url.Parse(pilotStub.URL) 140 if tt.pilotNotReachable { 141 stubURL, _ = url.Parse("http://notpilot") 142 } 143 c := &Command{ 144 Address: stubURL.Host, 145 Client: &http.Client{}, 146 } 147 err := c.Do(tt.method, tt.path, tt.body) 148 if (err == nil) && tt.wantError { 149 t.Errorf("Expected an error but received none") 150 } else if (err != nil) && !tt.wantError { 151 t.Errorf("Unexpected err: %v", err) 152 } 153 }) 154 } 155 }